#include <stdint.h>
#include <device/board.h>
#include "CFanCtlEnclosure.hpp"
#include <device/hal.h>
#include "cmsis_os.h"
#include <random.h>

CFanCtlEnclosure::CFanCtlEnclosure(const buddy::hw::InputPin &pin_tach,
    uint16_t min_rpm, uint16_t max_rpm)
    : CFanCtlCommon(min_rpm, max_rpm)
    , tachometer(pin_tach) {
    buddy::hw::fanPowerSwitch.set();
    set_pwm(0);
}

void CFanCtlEnclosure::tick() {
    const bool edge = tachometer.tick();
    uint8_t output_pwm = desired_pwm.load();

    if (desired_pwm == 0) {
        state = idle;
    }
    switch (state) {
    case idle:
        output_pwm = 0;
        if (desired_pwm > 0) {
            state = starting;
            edges = 0;
            ticks = 0;
        }
        break;
    case starting:
        output_pwm = 255;
        ticks++;
        edges += edge ? 1 : 0;
        if (ticks > start_timeout) {
            state = error_starting;
        } else if (edges >= start_edges) {
            state = rpm_stabilization;
            ticks = 0;
        }
        break;
    case rpm_stabilization:
        ticks++;
        if (ticks > rpm_delay) {
            state = running;
        }
        break;
    case running:
        if (!get_rpm_is_ok()) {
            state = error_running;
        }
        break;
    default: // error state
        if (get_rpm_is_ok()) {
            state = running;
        }
        break;
    }

    // Atomic store
    output_pwm_ = output_pwm;
}

bool CFanCtlEnclosure::Tachometer::tick() {
    bool current_level = static_cast<bool>(pin.read());
    bool edge = current_level ^ previous_level;
    edges += edge ? 1 : 0;
    previous_level = current_level;
    if (++ticks >= measurement_ticks_interval) {
        rpm = edges / 4 * 60;
        edges = 0;
        ticks = 0;
    }
    return edge;
}

bool CFanCtlEnclosure::set_pwm(uint16_t pwm) {
    if (selftest_mode) {
        selftest_initial_pwm = pwm > 255 ? 255 : static_cast<uint8_t>(pwm);
    } else {
        desired_pwm = pwm > 255 ? 255 : static_cast<uint8_t>(pwm);
    }
    return true;
}

void CFanCtlEnclosure::enter_selftest_mode() {
    if (selftest_mode) {
        return;
    }
    selftest_mode = true;
    selftest_initial_pwm = get_pwm();
}

void CFanCtlEnclosure::exit_selftest_mode() {
    if (!selftest_mode) {
        return;
    }
    selftest_mode = false;
    set_pwm(selftest_initial_pwm.load());
    selftest_initial_pwm = 0;
}

bool CFanCtlEnclosure::selftest_set_pwm(uint8_t pwm) {
    if (!selftest_mode) {
        return false;
    }
    desired_pwm = pwm; // Set PWM directly without set_pwm function
    return true;
}
